# gozerbot/users.py
#
#

""" bot users """

__status__ = "seen"

## gozerbot imports

from gozerbot.datadir import datadir
from gozerbot.utils.log import rlog
from gozerbot.utils.locking import lockdec
from gozerbot.utils.generic import stripident, stripidents
from gozerbot.utils.exception import handle_exception, exceptionmsg
from gozerbot.utils.generic import die, stripped
from gozerbot.utils.name import stripname
from gozerbot.persist.persist import Persist
from gozerbot.config import config
from gozerbot.jsonusers import JsonUsers

## basic imports

import re
import types
import os
import time
import thread
import sqlalchemy

## locks

deletelock = thread.allocate_lock()
deletelocked = lockdec(deletelock)

## defines

if not config['nodb']:
    from gozerbot.database.samodels import UserHost, User, Perms, Statuses
    from gozerbot.database.alchemy import Session, query, getuser, byname, eagerload, dblocked
else:
    def trans(func, *args, **kwargs):
        def transaction(*args, **kwargs):
            func(*args, **kwargs)
        return transaction

## DbUsers class

class DbUsers(object):

    """ sqlalchemy backed users. """

    def __init__(self, ddir=None):
        self.datadir = ddir or datadir

    ### Misc. Functions

    def size(self):
        """ return nr of users. """
        return int(query(User).count())
        
    def names(self):
        """ get names of all users. """
        a = query(User).all()
        return [n.name for n in a]

    @dblocked
    def merge(self, name, userhost):
        """ add userhosts to user with name. """
        user = byname(name)
        if user:
            if name not in user.userhosts:
                user.userhosts.append(userhost)
                rlog(10, 'users', "%s merged with %s" % (userhost, name))
                return True
        return False

    def usersearch(self, userhost):
        """ search for users with a userhost like the one specified. """
        n = query(UserHost).filter(UserHost.userhost.like('%%%s%%' % userhost)).all()
        return [(u.name, u.userhost) for u in n]

    ### Check functions

    def exist(self, name):
        """ see if user with <name> exists. """
        a = byname(name)
        if a: return True
        return False

    def allowed(self, userhost, perms, log=True):
        """ check if user with userhosts is allowed to execute perm command. """
        if not type(perms) == types.ListType: perms = [perms, ]
        if 'ANY' in perms: return ['ANY', ]
        res = None
        user = getuser(userhost)
        if user:
            if log: rlog(10, 'users', '%s userhost denied' % userhost)
            return res
        else:
            uperms = set(user.perms)
            sperms = set(perms)
            intersection = sperms.intersection(uperms)
            res = list(intersection) or None
        if not res and log: rlog(10, 'users', "%s perm %s denied" % (userhost, str(perms)))
        if res and log: rlog(10, 'users', 'allowed %s %s perm' % (userhost, str(perms)))
        return res

    def permitted(self, userhost, who, what):
        """ check if (who,what) is in users permit list. """
        user = getuser(userhost)
        res = False
        if user:
            if '%s %s' % (who, what) in user.permits: res = True
        return res

    def status(self, userhost, status):
        """ check if user with <userhost> has <status> set. """
        user = getuser(userhost)
        res = False
        if user:
            if status.upper() in user.statuses: res = True
        return res

    def gotuserhost(self, name, userhost):
        """ check if user has userhost. """
        user = byname(name)
        if user: return userhost in user.userhosts
        return False

    def gotperm(self, name, perm):
        """ check if user has permission. """
        user = byname(name)
        if user: return perm.upper() in user.perms
        return False

    def gotpermit(self, name, permit):
        """ check if user permits something.  permit is a [who, what] list. """ 
        user = byname(name)
        if user: return '%s %s' % permit in user.permits
        return False

    def gotstatus(self, name, status):
        """ check if user has status. """
        user = byname(name)
        if user: return status.upper() in user.statuses
        return False

    ### Get Functions

    def getname(self, userhost):
        """ get name of user belonging to <userhost>. """
        user = getuser(userhost)
        if user: return str(user.name)
        return ""

    def gethosts(self, userhost):
        """ return the userhosts of the user associated with the specified userhost. """
        user = getuser(userhost)
        if user: return list(user.userhosts)
        return []
    
    def getemail(self, userhost):
        """ return the email of the specified userhost. """
        user = getuser(userhost)
        if user:
            if user.email: return str(user.email[0])
        return ""

    def getperms(self, userhost):
        """ return permissions of user. """
        user = getuser(userhost)
        if user: return list(user.perms)
        return []

    def getpermits(self, userhost):
        """ return permits of the specified userhost. """
        user = getuser(userhost)
        if user: return list(user.permits)
        return []

    def getstatuses(self, userhost):
        """ return the list of statuses for the specified userhost. """
        user = getuser(userhost)
        if user: return list(user.statuses)

    def getuserhosts(self, name):
        """ return the userhosts associated with the specified user. """
        user = byname(name)
        if user: return list(user.userhosts)

    def getuseremail(self, name):
        """ get email of user. """
        user = byname(name)
        if user and user.email: return str(user.email[0])

    def getuserperms(self, name):
        """ return permission of user. """
        user = byname(name)
        if user: return list(user.perms)

    def getuserpermits(self, name):
        """ return permits of user. """
        user = byname(name)
        if user: return list(user.permits)

    def getuserstatuses(self, name):
        """ return the list of statuses for the specified user. """
        user = byname(name)
        if user: return list(user.statuses)

    def getpermusers(self, perm):
        """ return all users that have the specified perm. """
        n = query(Perms).filter(Perms.perm==perm.upper()).all()
        return [user.name for user in n]

    def getstatususers(self, status):
        """ return all users that have the specified status. """
        n = query(Statuses).filter(Statuses.status==status.upper()).all()
        return [user.name for user in n]

    ### Set Functions

    @dblocked
    def setemail(self, name, email):
        """ set email of user. """
        user = byname(name)
        if user:
            try: user.email.remove(email)
            except: pass
            user.email.insert(0, email)
            return True
        return False

    ### Add functions

    @dblocked
    def add(self, name, userhosts, perms):
        """ add an user. session argument is provided by @trans. """
        if type(userhosts) != types.ListType: rlog(10, 'users', 'i need a list of userhosts') ; return False
        name = stripname(name.lower())
        if not os.path.isdir(self.datadir + os.sep + 'users'): os.mkdir(self.datadir + os.sep + 'users')
        if not os.path.isdir(self.datadir + os.sep + 'users' +  os.sep + name): os.mkdir(self.datadir + os.sep + 'users' +  os.sep + name)
        user = byname(name)
        if not user:
            try:
                newuser = User(name=name)
                newuser.userhosts.extend(userhosts)
                newuser.perms.extend(perms)
                Session.add(newuser)
            except sqlalchemy.exc.IntegrityError, ex:
                if 'not unique' in str(ex):
                    rlog(10, 'users', '%s %s already exists' % (name, userhosts))
                    Session.close()
                    return True
                else: raise
            except Exception, ex: raise
            rlog(10, 'users', '%s %s %s added to user database' % (name, userhosts, perms))
        return True

    @dblocked
    def addemail(self, userhost, email):
        """ add an email address to the userhost. """
        user = getuser(userhost)
        if user:
            user.email.append(email)
            rlog(10, 'users', '%s (%s) added to email' % (email, userhost))
            return True
        return False

    @dblocked
    def addperm(self, userhost, perm):
        """ add the specified perm to the userhost. """
        user = getuser(userhost)
        if user:
            user.perms.append(perm.upper())
            rlog(10, 'users', '%s perm %s added' % (userhost, perm))
            return True
        return False

    @dblocked
    def addpermit(self, userhost, permit):
        """ add the given [who, what] permit to the given userhost. """
        user = getuser(userhost)
        if user:
            p = '%s %s' % permit
            user.permits.append(p)
            Session.refesh(user)
            rlog(10, 'users', '%s permit %s added' % (userhost, p))
            return True
        return False

    @dblocked
    def addstatus(self, userhost, status):
        """ add status to given userhost. """
        user = getuser(userhost)
        if user:
            user.statuses.append(status.upper())
            rlog(10, 'users', '%s status %s added' % (name, status))
            return True
        return False

    @dblocked
    def adduserhost(self, name, userhost):
        """ add userhost. """
        user = byname(name)
        if not user:
            user = User(name=name)
            Session.add(user)
        user.userhosts.append(userhost)
        rlog(10, 'users', '%s (%s) added to userhosts' % (name, userhost))
        return True

    @dblocked
    def adduseremail(self, name, email):
        """ add email to specified user. """
        user = byname(name)
        if user:
            user.email.append(email)
            rlog(10, 'users', '%s email %s added' % (name, email))
            return True

    @dblocked
    def adduserperm(self, name, perm):
        """ add permission. """
        user = byname(name)
        if user:
            perm = perm.upper()
            user.perms.append(perm)
            rlog(10, 'users', '%s perm %s added' % (name, perm))
            return True
        return False

    @dblocked
    def adduserpermit(self, name, who, permit):
        """ add (who, what) permit tuple to specified user. """
        user = byname(name)
        if user:
            p = '%s %s' % (who, permit)
            user.permits.append(p)
            rlog(10, 'users', '%s permit %s added' % (name, p))
            return True
        return False

    @dblocked
    def adduserstatus(self, name, status):
        """ add status to given user. """
        user = byname(name)
        if user:
            user.statuses.append(status.upper())
            rlog(10, 'users', '%s status %s added' % (name, status))
            return True
        return False

    @dblocked
    def addpermall(self, perm): 
        """ add permission to all users. """
        users = query(User).all()
        if users:
            for user in users: user.perms.append(perm.upper())

    ### Delete functions

    @dblocked
    def delemail(self, userhost, email):
        """ delete email from userhost. """
        user = getuser(userhost)
        if user:
            if email in user.emails:
                user.emails.remove(email)
                return True
        return False

    @dblocked
    def delperm(self, userhost, perm):
        """ delete perm from userhost. """
        user = getuser(userhost)
        if user:
            p = perm.upper()
            if p in user.perms:
                user.perms.remove(p)
                return True
        return False
            
    @dblocked
    def delpermit(self, userhost, permit):
        """ delete permit from userhost. """
        user = getuser(userhost)
        if user:
            p = '%s %s' % permit
            if p in user.permits:
                user.permits.remove(p)
                return True
        return False

    @dblocked
    def delstatus(self, userhost, status):
        """ delete status from userhost. """
        user = getuser(userhost)
        if user:
            st = status.upper()
            if st in user.statuses:
                user.statuses.remove(st)
                return True
        return False

    @dblocked
    def delete(self, name):
        """ delete user with name. """ 
        name = stripname(name)
        user = byname(name)
        if user:
            rlog(10, 'users', "deleting %s" % name)
            Session.delete(user)
            return True
        return False

    @dblocked
    def deluserhost(self, name, userhost):
        """ delete the userhost entry. """
        user = byname(name)
        if user:
            if userhost in user.userhosts:
                user.userhosts.remove(userhost)
                rlog(10, 'users', '%s userhost %s deleted' % (name, userhost))
                return True
        return False

    @dblocked
    def deluseremail(self, name, email):
        """ delete email. """
        user = byname(name)
        if user:
            if email in user.email:
                user.email.remove(email)
                rlog(10, 'users', '%s email %s deleted' % (name, email))
                return True
        return False

    @dblocked
    def deluserperm(self, name, perm):
        """ delete permission. """
        user = byname(name)
        if user:
            p = perm.upper()
            if p in user.perms:
                user.perms.remove(p)
                rlog(10, 'users', '%s perm %s deleted' % (name, p))
                return True
        return False

    @dblocked
    def deluserpermit(self, name, permit):
        """ delete permit. """
        user = byname(name)
        if user:
            p = '%s %s' % permit
            if p in user.permits:
                user.permits.remove(p)
                rlog(10, 'users', '%s permit %s deleted' % (name, p))
                return True
        return False

    @dblocked
    def deluserstatus(self, name, status):
        """ delete the status from the given user. """
        user = byname(name)
        if user:
            st = status.upper()
            if st in user.statuses:
                user.statuses.remove(status)
                rlog(10, 'users', '%s status %s deleted' % (name, st))
                return True
        return False

    @dblocked
    def delallemail(self, name):
        """ delete all emails of the specified user. """
        user = byname(name)
        if user:
            user.email = []
            rlog(10, 'users', '%s emails deleted' % (name, ))
            return True
        return False

    @dblocked
    def delpermall(self, perm):
        """ delete permission from all users. """
        users = query(User).options(eagerload('perms')).all()
        for user in users:
            if user.name != 'owner':
                try:
                    user.perms.remove(perm)
                except ValueError:
                    pass
        return True

    def make_owner(self, userhosts):
        """
            see if owner already has a user account if not merge otherwise 
            add.

        """
        owner = []
        if type(userhosts) != types.ListType: owner.append(userhosts)
        else: owner = userhosts
        for userhost in owner:
            username = self.getname(unicode(userhost))
            if not username:
                if not self.merge('owner', unicode(userhost)): self.add('owner', [unicode(userhost), ], ['USER', 'OPER'])
            elif username != 'owner':
                self.delete(username)
                if not self.merge('owner', unicode(userhost)): self.add('owner', [unicode(userhost), ], ['USER', 'OPER'])
               

## defines

if not config['nodb']: users = DbUsers()
else: users = JsonUsers(datadir + os.sep + (config['jsonuser'] or 'users.json'))

